`ifndef ETH_ARP_SVH
`define ETH_ARP_SVH
class eth_arp;
    virtual mac_bfm bfm;
    /*
     * 静态属性
     */
    arp_pck_s   arp_pck;
    function new(virtual mac_bfm b);
        bfm = b;
        arp_config();
    endfunction

    function void arp_config ();
        byte stream[];
        //bit [31:0] old_crc = 32'hff_ff_ff_ff;
        bit [31:0] old_crc = 32'h00_00_00_00;

        this.arp_pck.head.preamble = 64'h55_55_55_55_55_55_55_d5;
        this.arp_pck.head.dst_mac_addr = 48'hff_ff_ff_ff_ff_ff;
        this.arp_pck.head.src_mac_addr = PC_MAC_ADDR;
        this.arp_pck.head.ether_type = ARP;
        this.arp_pck.arp.hardware_type = 16'h0001;
        this.arp_pck.arp.protocal_type = 16'h0800;
        this.arp_pck.arp.handware_addr_len = 8'h06;
        this.arp_pck.arp.protocal_addr_len = 8'h04;
        this.arp_pck.arp.op_code = 16'h0001;
        this.arp_pck.arp.src_mac_addr = PC_MAC_ADDR;
        this.arp_pck.arp.src_ip_addr = PC_IP_ADDR;
        this.arp_pck.arp.dst_mac_addr = 48'h00_00_00_00_00_00;
        this.arp_pck.arp.dst_ip_addr = DEV_IP_ADDR;
        this.arp_pck.arp.pad = '0;

        stream = {>>{this.arp_pck}};
        // 去掉前导符和最后的4个校验字节
        for(int i = 8; i < $size(stream)-4; i = i + 1 )
        begin
            old_crc = calc_crc(stream[i], old_crc);
        end
        this.arp_pck.checksum = {old_crc[7:0], old_crc[15:8], old_crc[23:16], old_crc[31:24]};
    endfunction: arp_config

    function bit[32:0] calc_crc_d8(bit [7:0] data, bit [31:0] init);
        bit [7:0] d;
        bit [31:0] c;
        bit [31:0] newcrc;

        d = data;
        c = init;

        newcrc[0] = d[6] ^ d[0] ^ c[24] ^ c[30];
        newcrc[1] = d[7] ^ d[6] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[30] ^ c[31];
        newcrc[2] = d[7] ^ d[6] ^ d[2] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[26] ^ c[30] ^ c[31];
        newcrc[3] = d[7] ^ d[3] ^ d[2] ^ d[1] ^ c[25] ^ c[26] ^ c[27] ^ c[31];
        newcrc[4] = d[6] ^ d[4] ^ d[3] ^ d[2] ^ d[0] ^ c[24] ^ c[26] ^ c[27] ^ c[28] ^ c[30];
        newcrc[5] = d[7] ^ d[6] ^ d[5] ^ d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[27] ^ c[28] ^ c[29] ^ c[30] ^ c[31];
        newcrc[6] = d[7] ^ d[6] ^ d[5] ^ d[4] ^ d[2] ^ d[1] ^ c[25] ^ c[26] ^ c[28] ^ c[29] ^ c[30] ^ c[31];
        newcrc[7] = d[7] ^ d[5] ^ d[3] ^ d[2] ^ d[0] ^ c[24] ^ c[26] ^ c[27] ^ c[29] ^ c[31];
        newcrc[8] = d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[0] ^ c[24] ^ c[25] ^ c[27] ^ c[28];
        newcrc[9] = d[5] ^ d[4] ^ d[2] ^ d[1] ^ c[1] ^ c[25] ^ c[26] ^ c[28] ^ c[29];
        newcrc[10] = d[5] ^ d[3] ^ d[2] ^ d[0] ^ c[2] ^ c[24] ^ c[26] ^ c[27] ^ c[29];
        newcrc[11] = d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[3] ^ c[24] ^ c[25] ^ c[27] ^ c[28];
        newcrc[12] = d[6] ^ d[5] ^ d[4] ^ d[2] ^ d[1] ^ d[0] ^ c[4] ^ c[24] ^ c[25] ^ c[26] ^ c[28] ^ c[29] ^ c[30];
        newcrc[13] = d[7] ^ d[6] ^ d[5] ^ d[3] ^ d[2] ^ d[1] ^ c[5] ^ c[25] ^ c[26] ^ c[27] ^ c[29] ^ c[30] ^ c[31];
        newcrc[14] = d[7] ^ d[6] ^ d[4] ^ d[3] ^ d[2] ^ c[6] ^ c[26] ^ c[27] ^ c[28] ^ c[30] ^ c[31];
        newcrc[15] = d[7] ^ d[5] ^ d[4] ^ d[3] ^ c[7] ^ c[27] ^ c[28] ^ c[29] ^ c[31];
        newcrc[16] = d[5] ^ d[4] ^ d[0] ^ c[8] ^ c[24] ^ c[28] ^ c[29];
        newcrc[17] = d[6] ^ d[5] ^ d[1] ^ c[9] ^ c[25] ^ c[29] ^ c[30];
        newcrc[18] = d[7] ^ d[6] ^ d[2] ^ c[10] ^ c[26] ^ c[30] ^ c[31];
        newcrc[19] = d[7] ^ d[3] ^ c[11] ^ c[27] ^ c[31];
        newcrc[20] = d[4] ^ c[12] ^ c[28];
        newcrc[21] = d[5] ^ c[13] ^ c[29];
        newcrc[22] = d[0] ^ c[14] ^ c[24];
        newcrc[23] = d[6] ^ d[1] ^ d[0] ^ c[15] ^ c[24] ^ c[25] ^ c[30];
        newcrc[24] = d[7] ^ d[2] ^ d[1] ^ c[16] ^ c[25] ^ c[26] ^ c[31];
        newcrc[25] = d[3] ^ d[2] ^ c[17] ^ c[26] ^ c[27];
        newcrc[26] = d[6] ^ d[4] ^ d[3] ^ d[0] ^ c[18] ^ c[24] ^ c[27] ^ c[28] ^ c[30];
        newcrc[27] = d[7] ^ d[5] ^ d[4] ^ d[1] ^ c[19] ^ c[25] ^ c[28] ^ c[29] ^ c[31];
        newcrc[28] = d[6] ^ d[5] ^ d[2] ^ c[20] ^ c[26] ^ c[29] ^ c[30];
        newcrc[29] = d[7] ^ d[6] ^ d[3] ^ c[21] ^ c[27] ^ c[30] ^ c[31];
        newcrc[30] = d[7] ^ d[4] ^ c[22] ^ c[28] ^ c[31];
        newcrc[31] = d[5] ^ c[23] ^ c[29];
        return newcrc;
    endfunction

    function bit[32:0] calc_crc(bit [7:0] data, bit [31:0] init);
        reg [31:0] crc;
        reg        crc_feedback;
        integer    I;
        begin
            crc = ~ init;
            for (I = 0; I < 8; I = I + 1)
            begin
                crc_feedback = crc[0] ^ data[I];

                crc[0]       = crc[1];
                crc[1]       = crc[2];
                crc[2]       = crc[3];
                crc[3]       = crc[4];
                crc[4]       = crc[5];
                crc[5]       = crc[6]  ^ crc_feedback;
                crc[6]       = crc[7];
                crc[7]       = crc[8];
                crc[8]       = crc[9]  ^ crc_feedback;
                crc[9]       = crc[10] ^ crc_feedback;
                crc[10]      = crc[11];
                crc[11]      = crc[12];
                crc[12]      = crc[13];
                crc[13]      = crc[14];
                crc[14]      = crc[15];
                crc[15]      = crc[16] ^ crc_feedback;
                crc[16]      = crc[17];
                crc[17]      = crc[18];
                crc[18]      = crc[19];
                crc[19]      = crc[20] ^ crc_feedback;
                crc[20]      = crc[21] ^ crc_feedback;
                crc[21]      = crc[22] ^ crc_feedback;
                crc[22]      = crc[23];
                crc[23]      = crc[24] ^ crc_feedback;
                crc[24]      = crc[25] ^ crc_feedback;
                crc[25]      = crc[26];
                crc[26]      = crc[27] ^ crc_feedback;
                crc[27]      = crc[28] ^ crc_feedback;
                crc[28]      = crc[29];
                crc[29]      = crc[30] ^ crc_feedback;
                crc[30]      = crc[31] ^ crc_feedback;
                crc[31]      =           crc_feedback;
            end
            return ~ crc;
        end
    endfunction

    task send_arp();
        byte stream[];
        stream = {>>{this.arp_pck}};
        $display("arp frame length is %d", $size(stream));
        bfm.gmii_rx_dv = 0;
        bfm.gmii_rx_er = 0;
        bfm.gmii_rxd = 0;
        for(int i = 0; i < $size(stream); i = i + 1 )
        begin
            @ (negedge bfm.gmii_rx_clk);
            bfm.gmii_rx_dv = 1;
            bfm.gmii_rxd = stream[i];
        end
        @ (negedge bfm.gmii_rx_clk);
        bfm.gmii_rx_dv = 0;
    endtask:send_arp
endclass
`endif
